package com.varunest.sparkbutton;

import com.varunest.sparkbutton.custom.TypedAttrUtils;
import com.varunest.sparkbutton.helper.CircleComponent;
import com.varunest.sparkbutton.helper.DotsComponent;

import ohos.agp.animation.Animator;
import ohos.agp.animation.AnimatorGroup;
import ohos.agp.animation.AnimatorProperty;
import ohos.agp.animation.AnimatorValue;
import ohos.agp.utils.Color;
import ohos.agp.components.*;

import ohos.app.Context;
import ohos.global.resource.NotExistException;
import ohos.media.image.ImageSource;
import ohos.media.image.PixelMap;
import ohos.media.image.common.PixelFormat;
import ohos.multimodalinput.event.TouchEvent;

import java.io.IOException;
import java.io.InputStream;

/**
 * SparkButton
 *
 * @author varun 7th July 2016
 */
public class SparkButton extends DirectionalLayout implements Component.ClickedListener, Component.TouchEventListener {
    private static final int INVALID_RESOURCE_ID = -1;
    private static final float DOTVIEW_SIZE_FACTOR = 3;
    private static final float DOTS_SIZE_FACTOR = .1f;
    private static final float CIRCLEVIEW_SIZE_FACTOR = 1.4f;

    public int imageResourceIdActive = INVALID_RESOURCE_ID;
    public int imageResourceIdInactive = INVALID_RESOURCE_ID;

    public int imageSize = 30;
    public int dotsSize;
    public int circleSize;
    public int secondaryColor;
    public int primaryColor;
    public int activeImageTint;
    public int inActiveImageTint;
    public DotsComponent dotsComponent;
    public CircleComponent circleComponent;
    public Image imageView;
    public boolean isPressOnTouch = true;
    public float animationSpeed = 1;
    public boolean isChecked = false;

    private AnimatorGroup animatorSet;
    private SparkEventListener listener;
    private String imageFlag;

    /**
     * SparkButton
     *
     * @param context context
     */
    public SparkButton(Context context) {
        super(context);
    }

    /**
     * SparkButton
     *
     * @param context context
     * @param attrs attrs
     */
    public SparkButton(Context context, AttrSet attrs) {
        super(context, attrs);
        geneticist(attrs);
        init();
    }

    /**
     * SparkButton
     *
     * @param context context
     * @param attrs attrs
     * @param defStyleAttr defStyleAttr
     */
    public SparkButton(Context context, AttrSet attrs, String defStyleAttr) {
        super(context, attrs, defStyleAttr);
        geneticist(attrs);
        init();
    }

    /**
     * setColors
     *
     * @param startColor startColor
     * @param endColor endColor
     */
    public void setColors(int startColor, int endColor) {
        this.secondaryColor = startColor;
        this.primaryColor = endColor;
        updateView();
    }

    private void updateView() {
        circleComponent.setColors(secondaryColor, primaryColor);
        dotsComponent.setColors(secondaryColor, primaryColor);
    }

    /**
     * setAnimationSpeed
     *
     * @param animationSpeed animationSpeed
     */
    public void setAnimationSpeed(float animationSpeed) {
        this.animationSpeed = animationSpeed;
    }

    /**
     * init
     */
    void init() {
        circleSize = (int) (imageSize * CIRCLEVIEW_SIZE_FACTOR);
        dotsSize = (int) (imageSize * DOTVIEW_SIZE_FACTOR);

        DirectionalLayout parse = (DirectionalLayout) LayoutScatter.getInstance(
                getContext()).parse(ResourceTable.Layout_layout_spark_button, this, true);
        circleComponent = (CircleComponent) parse.findComponentById(ResourceTable.Id_vCircle);
        circleComponent.setWidth(circleSize);
        circleComponent.setHeight(circleSize);
        circleComponent.setColors(secondaryColor, primaryColor);

        dotsComponent = (DotsComponent) parse.findComponentById(ResourceTable.Id_vDotsView);
        dotsComponent.setColors(secondaryColor, primaryColor);
        dotsComponent.setMaxDotSize((int) (imageSize * DOTS_SIZE_FACTOR));
        dotsComponent.setWidth(dotsSize);
        dotsComponent.setHeight(dotsSize);

        imageView = (Image) parse.findComponentById(ResourceTable.Id_ivImage);
        imageView.setHeight(imageSize);
        imageView.setWidth(imageSize);
        imageView.setPixelMap(imageResourceIdInactive);
        imageView.setClickedListener(this);
        imageView.setTouchEventListener(this);
    }

    /**
     * Call this function to start spark animation
     */
    public void playAnimation() {
        if (animatorSet != null) {
            animatorSet.cancel();
        } else {
            animatorSet = new AnimatorGroup();
        }
        initAnimation();
    }

    private void initAnimation() {
        AnimatorProperty starScaleAnimator = imageView.createAnimatorProperty();
        starScaleAnimator.scaleXFrom(0.2f).scaleYFrom(0.2f).scaleYBy(0.8f).scaleXBy(0.8f).setDuration(500);

        AnimatorValue dotsAnimator = new AnimatorValue();
        dotsAnimator.setDuration(500);
        dotsAnimator.setCurveType(Animator.CurveType.LINEAR);
        dotsAnimator.setValueUpdateListener((animatorValue, v) -> dotsComponent.setCurrentProgress(v));

        AnimatorValue innerCircleAnimator = new AnimatorValue();
        innerCircleAnimator.setDuration(500);
        innerCircleAnimator.setCurveType(Animator.CurveType.LINEAR);
        innerCircleAnimator.setValueUpdateListener((animatorValue, v) -> {
            circleComponent.setInnerCircleRadiusProgress(v);
            circleComponent.setOuterCircleRadiusProgress(v);
        });
        animatorSet.runParallel(innerCircleAnimator, starScaleAnimator, dotsAnimator);
        if (isChecked || !isPressOnTouch) {
            animatorSet.start();
        } else {
            imageView.setScaleX(1);
            imageView.setScaleY(1);
        }
        initListener();
    }

    private void initListener() {
        animatorSet.setStateChangedListener(new Animator.StateChangedListener() {
            @Override
            public void onStart(Animator animator) {
                if (listener != null) {
                    listener.onEvent(imageView, isChecked);
                }
            }

            @Override
            public void onStop(Animator animator) {
            }

            @Override
            public void onCancel(Animator animator) {
                circleComponent.setInnerCircleRadiusProgress(0);
                circleComponent.setOuterCircleRadiusProgress(0);
                dotsComponent.setCurrentProgress(0);
                imageView.setScaleX(1);
                imageView.setScaleY(1);
            }

            @Override
            public void onEnd(Animator animator) {
                if (listener != null) {
                    listener.onEvent(imageView, isChecked);
                }
            }

            @Override
            public void onPause(Animator animator) {
            }

            @Override
            public void onResume(Animator animator) {
            }
        });
    }

    /**
     * Returns whether the button is checked (Active) or not.
     *
     * @return boolean
     */
    public boolean isChecked() {
        return isChecked;
    }

    /**
     * Change Button State (Works only if both active and disabled image resource is defined)
     *
     * @param isFlag desired checked state of the button
     */
    public void setChecked(boolean isFlag) {
        isChecked = isFlag;
        imageView.setPixelMap(getPixelMap(isChecked ? imageResourceIdActive : imageResourceIdInactive));
    }

    /**
     * setImageFlag
     *
     * @param flag flag
     */
    public void setImageFlag(String flag) {
        this.imageFlag = flag;
    }

    /**
     * setEventListener
     *
     * @param listener listener
     */
    public void setEventListener(SparkEventListener listener) {
        this.listener = listener;
    }

    /**
     * pressOnTouch
     *
     * @param isPressOnTouch pressOnTouch
     */
    public void pressOnTouch(boolean isPressOnTouch) {
        this.isPressOnTouch = isPressOnTouch;
    }

    private void geneticist(AttrSet attr) {
        imageSize = TypedAttrUtils.getInteger(attr, "sparkbutton_iconSize", imageSize);
        imageFlag = TypedAttrUtils.getString(attr, "sparkbutton_flag", "heart");
        switch (imageFlag) {
            case "facebook":
                imageResourceIdActive = ResourceTable.Media_ic_thumb;
                imageResourceIdInactive = ResourceTable.Media_ic_thumb;
                break;
            case "star":
                imageResourceIdActive = ResourceTable.Media_ic_star_on;
                imageResourceIdInactive = ResourceTable.Media_ic_star_off;
                break;
            case "twitter":
                imageResourceIdActive = ResourceTable.Media_ic_twitter;
                imageResourceIdInactive = ResourceTable.Media_ic_twitter;
                break;
            default:
                imageResourceIdActive = ResourceTable.Media_ic_heart_on;
                imageResourceIdInactive = ResourceTable.Media_ic_heart_off;
                break;
        }
        isPressOnTouch = TypedAttrUtils.getBoolean(
                attr, "sparkbutton_pressOnTouch", true);
        animationSpeed = TypedAttrUtils.getDimensionPixelSize(
                attr, "sparkbutton_animationSpeed", 1);
        primaryColor = Color.getIntColor(TypedAttrUtils.getString(
                attr, "sparkbutton_primaryColor", "#FFC107"));
        secondaryColor = Color.getIntColor(TypedAttrUtils.getString(
                attr, "sparkbutton_secondaryColor", "#FF5722"));
        activeImageTint = Color.getIntColor(TypedAttrUtils.getString(
                attr, "sparkbutton_activeImageTint", "#00000000"));
        inActiveImageTint = Color.getIntColor(TypedAttrUtils.getString(
                attr, "sparkbutton_inActiveImageTint", "#00000000"));
    }

    /**
     * onClick
     *
     * @param component component
     */
    @Override
    public void onClick(Component component) {
        if (imageResourceIdInactive != INVALID_RESOURCE_ID) {
            setChecked(!isChecked);
            if (animatorSet != null) {
                animatorSet.cancel();
            }
            if (isChecked || !isPressOnTouch) {
                circleComponent.setVisibility(VISIBLE);
                dotsComponent.setVisibility(VISIBLE);
                playAnimation();
            } else {
                dotsComponent.setVisibility(VERTICAL);
                circleComponent.setVisibility(VERTICAL);
                imageView.setScale(1,1);
            }
        } else {
            playAnimation();
        }
        if (listener != null) {
            listener.onEvent(imageView, isChecked);
        }
    }

    private PixelMap getPixelMap(int drawableId) {
        InputStream drawableInputStream = null;
        try {
            drawableInputStream = getResourceManager().getResource(drawableId);
            ImageSource.SourceOptions sourceOptions = new ImageSource.SourceOptions();
            ImageSource imageSource = ImageSource.create(drawableInputStream, sourceOptions);
            ImageSource.DecodingOptions decodingOptions = new ImageSource.DecodingOptions();
            decodingOptions.desiredPixelFormat = PixelFormat.ARGB_8888;
            return imageSource.createPixelmap(decodingOptions);
        } catch (IOException | NotExistException e) {
            e.getMessage();
        } finally {
            if (drawableInputStream != null) {
                try {
                    drawableInputStream.close();
                } catch (IOException e) {
                    e.getMessage();
                }
            }
        }
        return null;
    }

    /**
     * onTouchEvent
     *
     * @param component component
     * @param touchEvent touchEvent
     * @return boolean
     */
    @Override
    public boolean onTouchEvent(Component component, TouchEvent touchEvent) {
        if (touchEvent.getAction() == TouchEvent.PRIMARY_POINT_DOWN) {
            imageView.setScale(0.8f, 0.8f);
        } else if (touchEvent.getAction() == TouchEvent.PRIMARY_POINT_UP) {
            if (isPressOnTouch) {
                imageView.setScale(1f, 1f);
            } else {
                playAnimation();
            }
        }
        return true;
    }
}
